//
//  KDSliderControl.m
//  KDStatusSliderButton
//
//  Created by Keith Duncan on 26/01/2008.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
//

//
// Event loop in -mouseDown: contributed by Jonathan Dann
// It replaced my massive loop and is more functional
//
#import "KDSliderControl.h"

@implementation KDSliderControl

+ (Class)cellClass {
	return [KDGradientCell class];
}

- (id)initWithFrame:(NSRect)frame {
	[super initWithFrame:frame];
	_floatValue = 0.0;
	_state = NO;
	
	return self;
}

- (void)setFloatValue:(float)value 
{
	_floatValue = value;
	
	[self setNeedsDisplay:YES];
}

- (void)setValue:(NSNumber*)value
{
	[self setFloatValue:[value floatValue]];
}

- (void)viewWillMoveToSuperview:(NSView *)view {
	[super viewWillMoveToSuperview:view];
	
	[[NSNotificationCenter defaultCenter] removeObserver:self];
	
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(display) name:NSWindowDidResignKeyNotification object:[view window]];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(display) name:NSWindowDidBecomeKeyNotification object:[view window]];
	
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(display) name:NSApplicationDidBecomeActiveNotification object:NSApp];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(display) name:NSApplicationDidResignActiveNotification object:NSApp];
}

NS_INLINE NSRect InsetTextRect(NSRect textRect) {
	return NSInsetRect(textRect, 0, NSHeight(textRect)/7.0);
}

NS_INLINE void PartRects(NSRect bounds, NSRect *textRects, NSRect *backgroundRect) {
	NSDivideRect(bounds, textRects, backgroundRect, NSWidth(bounds)/5.0, NSMinXEdge);
	
	textRects[1] = InsetTextRect(NSOffsetRect(textRects[0], NSWidth(*backgroundRect), 0));
	textRects[0] = InsetTextRect(textRects[0]);
		
	(*backgroundRect).size.width -= NSWidth(*textRects);
	*backgroundRect = NSInsetRect(*backgroundRect, NSWidth(bounds)/20.0, 0);
}

NS_INLINE NSRect InsetBackgroundRect(NSRect backgroundRect) {
	return NSInsetRect(backgroundRect, 1.0, 1.0);
}

NS_INLINE CGFloat BackgroundRadiusForRect(NSRect rect) {
	return (4.0/27.0)*NSHeight(rect);
}

NS_INLINE NSRect KnobRectForInsetBackground(NSRect slotRect, float floatValue) {
	CGFloat knobWidth = NSWidth(slotRect)*(4.0/9.0);
	//return RectFromCentrePoint(NSMakePoint(NSMinX(slotRect) + (knobWidth/2.0) + (floatValue*(NSWidth(slotRect)-knobWidth)), NSMidY(slotRect)), NSMakeSize(knobWidth, NSHeight(slotRect)));
	NSPoint p = NSMakePoint(NSMinX(slotRect) + (knobWidth/2.0) + (floatValue*(NSWidth(slotRect)-knobWidth)), NSMidY(slotRect));
	NSSize s = NSMakeSize(knobWidth, NSHeight(slotRect));
	return (NSRect){(NSPoint){p.x - (s.width/2.0), p.y - (s.height/2.0)}, (NSSize)s};

}

- (void)drawRect:(NSRect)frame {
	NSRect textRects[2], backgroundRect;
	PartRects([self bounds], textRects, &backgroundRect);
		
	if ([self needsToDrawRect:textRects[0]] || [self needsToDrawRect:textRects[1]]) {		
		NSShadow *textShadow = [[NSShadow alloc] init];
		[textShadow setShadowColor:[NSColor whiteColor]];
		[textShadow setShadowOffset:NSMakeSize(0, -1.5)];
		[textShadow setShadowBlurRadius:0.0];
				
		[NSGraphicsContext saveGraphicsState];
		[textShadow set];
		
		[[NSColor colorWithCalibratedWhite:(74.0/255.0) alpha:1.0] set];
		AFDrawStringAlignedInFrame(@"OFF", [NSFont boldSystemFontOfSize:0], NSCenterTextAlignment, textRects[0]);
		
		[NSGraphicsContext restoreGraphicsState];
		
		[NSGraphicsContext saveGraphicsState];
		[textShadow set];
		
		//NSUInteger state = [[self valueForBinding:NSValueBinding] unsignedIntegerValue];
	
#if 1
		if (_state == NSOnState && [[self window] isKeyWindow]) {
			[[NSColor colorWithCalibratedRed:(25.0/255.0) green:(86.0/255.0) blue:(173.0/255.0) alpha:1.0] set];
		} else {
			[[NSColor colorWithCalibratedWhite:(74.0/255.0) alpha:1.0] set];
		}
		
		AFDrawStringAlignedInFrame(@"ON", [NSFont boldSystemFontOfSize:0], NSCenterTextAlignment, textRects[1]);
#else
		if ((_state == NSOnState && [[self window] isKeyWindow])) {
			NSColor *topColor = [NSColor colorWithCalibratedRed:(93.0/255.0) green:(148.0/255.0) blue:(214.0/255.0) alpha:1.0];
			NSColor *endColor = [NSColor colorWithCalibratedRed:(25.0/255.0) green:(86.0/255.0) blue:(173.0/255.0) alpha:1.0];
			
			[[[NSGradient alloc] initWithStartingColor:topColor endingColor:endColor] drawInBezierPath:textPath angle:-90.0]];
		} else {
			NSColor *topColor = [NSColor colorWithCalibratedRed:(161.0/255.0) green:(176.0/255.0) blue:(207.0/255.0) alpha:1.0];
			NSColor *endColor = [NSColor colorWithCalibratedRed:(113.0/255.0) green:(133.0/255.0) blue:(171.0/255.0) alpha:1.0];
			
			[[[NSGradient alloc] initWithStartingColor:topColor endingColor:endColor] drawInBezierPath:textPath angle:-90.0]];
		}
		
#endif
		
		[NSGraphicsContext restoreGraphicsState];
		[textShadow release];
	}
	
	CGFloat radius = 0;
	NSBezierPath *backgroundPath = nil;
	NSGradient *backgroundGradient = nil;
	
	radius = BackgroundRadiusForRect(backgroundRect);
	backgroundPath = [NSBezierPath bezierPathWithRoundedRect:backgroundRect xRadius:radius yRadius:radius];
	backgroundGradient = [[NSGradient alloc] initWithColors:[NSArray arrayWithObjects:[NSColor colorWithCalibratedWhite:(80.0/255.0) alpha:0.9], [NSColor colorWithCalibratedWhite:(129.0/255.0) alpha:0.9], nil]];
	
	[backgroundGradient drawInBezierPath:backgroundPath angle:-90];
	[backgroundGradient release];
	
	NSRect insetBackgroundRect = InsetBackgroundRect(backgroundRect);
	
	radius = BackgroundRadiusForRect(insetBackgroundRect);
	backgroundPath = [NSBezierPath bezierPathWithRoundedRect:insetBackgroundRect xRadius:radius yRadius:radius];
	backgroundGradient = [[NSGradient alloc] initWithColorsAndLocations:[NSColor colorWithCalibratedWhite:(99.0/255.0) alpha:1.0], 0.0, [NSColor colorWithCalibratedWhite:(142.0/255.0) alpha:1.0], 0.3, [NSColor colorWithCalibratedWhite:(171.0/255.0) alpha:1.0], 1.0, nil];
	
	[backgroundGradient drawInBezierPath:backgroundPath angle:-90];
	[backgroundGradient release];
	
	[NSGraphicsContext saveGraphicsState];
	[backgroundPath setClip];
	
	// Draw the knob
	[self _drawKnobInSlotRect:insetBackgroundRect radius:radius];
	
	[NSGraphicsContext restoreGraphicsState];
}

- (BOOL)acceptsFirstMouse:(NSEvent *)event {
	return YES;
}

- (void)mouseDown:(NSEvent *)event {
	NSRect textRect, backgroundRect;
	PartRects([self bounds], &textRect, &backgroundRect);
	
	NSRect slotRect = InsetBackgroundRect(backgroundRect);
	NSRect knobRect = KnobRectForInsetBackground(slotRect, _floatValue);
	
	NSPoint hitPoint = [self convertPoint:[event locationInWindow] fromView:nil];
	//BOOL state = self.state;
	
	if (![self mouse:hitPoint inRect:knobRect]) {
		if ((_state == NSOffState && NSMaxX(knobRect) < hitPoint.x) || (_state == NSOnState && NSMinX(knobRect) > hitPoint.x)) [self quickSet:!_state];//[self setValue:[NSNumber numberWithUnsignedInteger:!state] forBinding:NSValueBinding];
		//[self quickSet:!_state];
		return;
	}
	
	[[self cell] setHighlighted:YES];
	
	BOOL loop = YES, dragging = NO;
	
	NSPoint mouseLocation;
	
	while (loop) {
		event = [[self window] nextEventMatchingMask:(NSLeftMouseUpMask|NSLeftMouseDraggedMask)];
		mouseLocation = [self convertPoint:[event locationInWindow] fromView:nil];
		
		CGFloat newPosition;
		CGFloat minPosition = NSMinX(slotRect) + NSWidth(knobRect)/2.0;
		CGFloat maxPosition = NSMaxX(slotRect) - NSWidth(knobRect)/2.0;
		
		switch ([event type]) {
			case NSLeftMouseDragged:
				dragging = YES;
				newPosition = mouseLocation.x - (hitPoint.x-NSMidX(knobRect));
				
				if (newPosition <= minPosition)
					_floatValue = 0.0;
				else if (newPosition >= maxPosition)
					_floatValue = 1.0;
				else
					_floatValue = (newPosition-minPosition)/(maxPosition - minPosition);
			
				[self setNeedsDisplay:YES];
				break;
			case NSLeftMouseUp:
				[[self cell] setHighlighted:NO];
				
				if (dragging) {
					if (_floatValue >= 0.15 && _state == NO) 
						[self setState:YES];
					else if (_floatValue <= 0.85 && _state == YES) 
						[self setState:NO];
					else 
						[self setState:_state];
				} 
				else 
					[self quickSet:!_state];
				
				loop = NO;
				break;
		}
	}
}

-(BOOL)getState
{
	return _state;
}

- (void)setState:(BOOL)value 
{
	_state = value;
	if(_state)
	{
		_floatValue = 1.00;
		if(![self isDaemonRunning]) [self launchDaemon];
	}
	else
	{
		
		_floatValue = 0.00;
		if([self isDaemonRunning]) [self terminateDaemon];
	}
}

- (void)quickSet:(BOOL)value
{
	float i;
	_state = value;
	if(_state)
	{
		for(i = 0.0; i<=1.1; i=i+.1)
		{ 
			[self performSelector:@selector(setValue:) withObject:[NSNumber numberWithFloat:i] afterDelay:.4];
		}
		if(![self isDaemonRunning]) [self launchDaemon];
	}
	else
	{
		for(i = 1.0; i>=-.10; i=i-.1)
		{ 
			[self performSelector:@selector(setValue:) withObject:[NSNumber numberWithFloat:i] afterDelay:.4];
		}		
		if([self isDaemonRunning]) [self terminateDaemon];
	}
}

-(BOOL)isDaemonRunning
{
	return ([AGProcess processForCommand:@"AppHider Daemon"] != nil);
}

-(void)launchDaemon
{
	[[NSWorkspace sharedWorkspace] launchApplication:[[[NSBundle bundleWithIdentifier:@"com.mettamedia.AppHider"] bundlePath] stringByAppendingString:@"/Contents/MacOS/AppHider Daemon.app"]];//[[NSWorkspace sharedWorkspace] launchAppWithBundleIdentifier:@"com.mettamedia.AppHiderDaemon" options:NSWorkspaceLaunchAsync additionalEventParamDescriptor:nil launchIdentifier:nil];
}

-(void)terminateDaemon
{
	OSStatus err;
	AppleEvent event, reply;
	
	const char *bundleIDString = [@"com.mettamedia.AppHiderDaemon" UTF8String];
	
	err = AEBuildAppleEvent(kCoreEventClass, kAEQuitApplication,
							typeApplicationBundleID, 
							bundleIDString, strlen(bundleIDString),
							kAutoGenerateReturnID, kAnyTransactionID,
							&event, NULL, "");
	
	if (err == noErr) {
		err = AESendMessage(&event, &reply, kAENoReply, kAEDefaultTimeout);
		(void)AEDisposeDesc(&event);
	}
	else
	{
		NSException* myException = [NSException
									exceptionWithName:@"DaemonCouldNotQuit"
									reason:@"Daemon was unable to quit using apple events"
									userInfo:nil];
		@throw myException;
	}
}


@end

@implementation KDSliderControl (Private)

- (void)_drawKnobInSlotRect:(NSRect)slotRect radius:(CGFloat)radius {
	NSRect handleBounds = KnobRectForInsetBackground(slotRect, _floatValue);
	
	((KDGradientCell *)_cell).cornerRadius = radius;
	
	[_cell drawBezelWithFrame:handleBounds inView:self];
}

@end
